﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SCMS.Model;
using System.Data;
using System.Data.Objects.DataClasses;
using System.Transactions;
using SCMS.CoreBusinessLogic.NotificationsManager;
using SCMS.Utils.DTOs;

namespace SCMS.CoreBusinessLogic.PurchaseOrder
{
    public class PurchaseOrderService : IPurchaseOrderService
    {

        private INotificationService NotificationService;
        private _ExchangeRate.IExchangeRateService exchangeRateService;

        public PurchaseOrderService(INotificationService NotificationService, _ExchangeRate.IExchangeRateService exchangeRateService)
        {
            this.NotificationService = NotificationService;
            this.exchangeRateService = exchangeRateService;
        }

        public bool SavePuchaseOrder(Model.PurchaseOrder entity, EntityCollection<Model.PurchaseOrderItem> poItemColl)
        {
            using (var dbContext = new SCMSEntities())
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    if (entity.OrderRequestId.Equals(Guid.Empty))
                        entity.OrderRequestId = dbContext.TenderAnalysis.SingleOrDefault(p => p.Id == entity.TenderAnalysisId).OrderRequestId;
                    entity.CurrencyId = dbContext.OrderRequests.SingleOrDefault(o => o.Id == entity.OrderRequestId).CurrencyId;
                    dbContext.PurchaseOrders.AddObject(entity);
                    if (dbContext.SaveChanges() > 0)
                    {
                        foreach (PurchaseOrderItem item in poItemColl)
                        {
                            dbContext.PurchaseOrderItems.AddObject(item);
                            if (!(dbContext.SaveChanges() > 0))
                            { scope.Dispose(); return false; }
                        }
                        scope.Complete();
                        return true;
                    }
                    else { scope.Dispose(); return false; }
                }
            }
        }

        public bool SavePuchaseOrder(Model.PurchaseOrder entity)
        {
            using (var context = new SCMSEntities())
            {
                if (entity.Id.Equals(Guid.Empty))
                {
                    entity.Id = Guid.NewGuid();
                    context.PurchaseOrders.AddObject(entity);
                }
                else
                {
                    var po = context.PurchaseOrders.FirstOrDefault(p => p.Id == entity.Id);
                    po.CurrencyId = entity.CurrencyId;
                    po.DeliveryAddress = entity.DeliveryAddress;
                    po.LatestDeliveryDate = entity.LatestDeliveryDate;
                    po.PaymentTermId = entity.PaymentTermId;
                    po.PODate = entity.PODate;
                    po.ProjectDonorId = entity.ProjectDonorId;
                    po.QuotationRef = entity.QuotationRef;
                    po.Remarks = entity.Remarks;
                    po.ShippingTermId = entity.ShippingTermId;
                    po.SupplierId = entity.SupplierId;
                }
                return context.SaveChanges() > 0;
            }
        }

        public bool SavePOItem(Model.PurchaseOrderItem entity)
        {
            using (var context = new SCMSEntities())
            {
                if (entity.Id.Equals(Guid.Empty))
                {
                    entity.Id = Guid.NewGuid();
                    context.PurchaseOrderItems.AddObject(entity);
                }
                else
                {
                    var poItem = context.PurchaseOrderItems.FirstOrDefault(p => p.Id == entity.Id);
                    //update purchase order total
                    poItem.PurchaseOrder.TotalAmount -= poItem.TotalPrice;
                    poItem.PurchaseOrder.TotalAmount += entity.TotalPrice;

                    poItem.BudgetLineId = entity.BudgetLineId;
                    poItem.Quantity = entity.Quantity;
                    poItem.Remarks = entity.Remarks;
                    poItem.TotalPrice = entity.TotalPrice;
                    poItem.UnitPrice = entity.UnitPrice;
                }
                return context.SaveChanges() > 0;
            }
        }

        public void DeletePOItem(Model.PurchaseOrderItem entity)
        {
            using (var context = new SCMSEntities())
            {
                var poItem = context.PurchaseOrderItems.FirstOrDefault(p => p.Id == entity.Id);
                poItem.PurchaseOrder.TotalAmount-=entity.TotalPrice;
                context.PurchaseOrderItems.DeleteObject(poItem);
                context.SaveChanges();
                var poItemCount = context.PurchaseOrderItems.Where(p => p.PurchaseOrderId == entity.PurchaseOrderId).Count();
                if (poItemCount == 0)
                {
                    var po = context.PurchaseOrders.FirstOrDefault(p => p.Id == entity.PurchaseOrderId);
                    po.OrderRequestId = null;
                    context.SaveChanges();
                }
            }
        }

        public List<Model.OrderRequest> GetPOrderORs(Guid PoId)
        {
            using (var context = new SCMSEntities())
            {
                var orderRequests = context.OrderRequests.Where(o => o.OrderRequestItems.Where(i => i.PurchaseOrderItems.Where(p => p.PurchaseOrderId == PoId).Count() > 0).Count() > 0).ToList();
                foreach (var or in orderRequests)
                {
                    var pd = or.ProjectDonor;
                }
                return orderRequests;
            }
        }

        public List<Model.PurchaseOrderItem> GetPOItemsByOrId(Guid PoId, Guid OrId)
        {
            using (var context = new SCMSEntities())
            {
                var poItems = context.PurchaseOrderItems.Where(p => p.PurchaseOrderId == PoId && p.OrderRequestItem.OrderRequestId == OrId).ToList();
                foreach (var poItem in poItems)
                {
                    var item = poItem.OrderRequestItem.Item;
                    var pb = poItem.ProjectBudget;
                }
                return poItems;
            }
        }

        public void DeletePO(Guid PoId)
        {
            using (var context = new SCMSEntities())
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        var poItems = context.PurchaseOrderItems.Where(p => p.PurchaseOrderId == PoId).ToList();
                        context.PurchaseOrderItems.Delete(poItems);
                        var po = context.PurchaseOrders.FirstOrDefault(p => p.Id == PoId);
                        context.PurchaseOrders.DeleteObject(po);
                        if (context.SaveChanges() > 0)
                            scope.Complete();
                        else
                            scope.Dispose();
                    }
                    catch (Exception ex)
                    {
                        scope.Dispose();
                        throw ex;
                    }
                }
            }
        }

        public string GenerateUniquNumber(CountryProgramme cp)
        {
            string code = "PO/DRC/" + cp.Country.ShortName + "/";
            string refNumber = "";
            long count = 1;
            using (var dbContext = new SCMSEntities())
            {
                var total = dbContext.PurchaseOrders.Where(p => p.CountryProgrammeId == cp.Id && p.IsSubmitted == true).Count();
                //Model.PurchaseOrder m = dbContext.PurchaseOrders.OrderByDescending(p => p.RecordCount).FirstOrDefault();
                //if (m != null)
                //    count = m.RecordCount + 1;
                count = total;
                Model.PurchaseOrder m =  null;
                do
                {
                    count++;
                    if (count < 10000)
                    {
                        if (count < 10)
                            refNumber = code + "0000" + count;
                        if (count < 100 & count >= 10)
                            refNumber =  code + "000" + count;
                        if (count < 1000 & count >= 100)
                            refNumber = code + "00" + count;
                        if (count < 10000 & count >= 1000)
                            refNumber = code + "0" + count;
                    }
                    m = dbContext.PurchaseOrders.FirstOrDefault(p => p.RefNumber == refNumber);
                } while (m != null);
                return refNumber;
            }
        }

        public List<Model.PurchaseOrder> GetPOs(Guid cpId)
        {
            using (var dbContext = new SCMSEntities())
            {
                List<Model.PurchaseOrder> PO = dbContext.PurchaseOrders.Where(p => p.CountryProgrammeId == cpId).OrderByDescending(p => p.PreparedOn).ToList();
                
                foreach (Model.PurchaseOrder item in PO)
                {
                    Model.OrderRequest or = item.OrderRequest;
                    Model.Location loc = item.Location;
                    Model.TenderAnalysi ta = item.TenderAnalysi;
                }
                return PO;
            }
        }

        public List<Model.ProjectBudget> GetProjectBudgets()
        {
            using (var context = new SCMSEntities())
            {
                List<Model.ProjectBudget> mm = context.ProjectBudgets.ToList();
                return mm;
            }
        }

        public List<Model.OrderRequest> GetOrderRequests(Guid countryprogramId)
        {
            using (var context = new SCMSEntities())
            {

                List<Model.OrderRequest> OR = new List<Model.OrderRequest>();
                OR = context.OrderRequests.Where(p => p.CountryProgrammeId == countryprogramId && p.IsAuthorized == true).ToList();
                foreach (Model.OrderRequest item in OR)
                {
                    EntityCollection<OrderRequestItem> oRit = item.OrderRequestItems;
                }
                return OR;
            }
        }

        public List<Model.TenderAnalysi> GetTenderAnalysis(Guid countryprogramId)
        {
            using (var context = new SCMSEntities())
            {

                List<Model.TenderAnalysi> TA = context.TenderAnalysis.Where(p => p.CountryProgrammeId == countryprogramId & p.SupplierSelected == true && p.PurchaseOrders.Count == 0).ToList();
                foreach (TenderAnalysi item in TA)
                {
                    Model.OrderRequest or = item.OrderRequest;

                    EntityCollection<TenderBidder> hh = item.TenderBidders;

                }
                return TA;
            }
        }

        public List<Model.PurchaseOrder> GetPurchaseOrders(Guid countryProgId)
        {
            using (var context = new SCMSEntities())
            {
                return context.PurchaseOrders.Where(p => p.CountryProgrammeId == countryProgId && p.IsAuthorized == true).ToList();
            }
        }

        public Model.PurchaseOrder GetPurchaseOrderById(Guid Id)
        {
            using (var context = new SCMSEntities())
            {
                var po = context.PurchaseOrders.FirstOrDefault(p => p.Id == Id);
                var ta = po.TenderAnalysi;
                if (po.OrderRequest != null)
                {
                    var cur = po.OrderRequest.Currency;
                    var pd = po.OrderRequest.ProjectDonor;
                }
                if (po.Currency != null)
                {
                    var cur = po.Currency;
                }
                var pd1 = po.ProjectDonor;
                var loc = po.Location;
                var supplier = po.Supplier;
                var pt = po.PaymentTerm;
                var st = po.ShippingTerm;
                if (po.Staff != null)
                {
                    var person = po.Staff.Person;
                    var desg = po.Staff.Designation;
                }
                if (po.Staff1 != null)
                {
                    var person = po.Staff1.Person;
                    var desg = po.Staff1.Designation;
                }
                if (po.Staff2 != null)
                {
                    var person = po.Staff2.Person;
                    var desg = po.Staff2.Designation;
                }
                return po;
            }
        }

        public List<Model.PurchaseOrderItem> GetPurchaseOrderItems(Guid purchaseOrderId)
        {
            using (var context = new SCMSEntities())
            {
                var poItems = context.PurchaseOrderItems.Where(p => p.PurchaseOrderId == purchaseOrderId).OrderByDescending(p => p.OrderRequestItemId).ToList();
                foreach (var po in poItems)
                {
                    if (po.OrderRequestItem != null)
                    {
                        var item = po.OrderRequestItem.Item;
                        var uom = po.OrderRequestItem.Item.UnitOfMeasure;
                    }
                    var pd = po.ProjectBudget.BudgetCategory.ProjectDonor;
                }
                return poItems;
            }
        }

        public Model.PurchaseOrderItem GetPurchaseOrderItemById(Guid id)
        {
            using (var context = new SCMSEntities())
            {
                var poItem = context.PurchaseOrderItems.FirstOrDefault(p => p.Id == id);
                if (poItem.OrderRequestItem != null)
                {
                    var item = poItem.OrderRequestItem.Item;
                }
                var po = poItem.PurchaseOrder;
                return poItem;
            }
        }

        /// <summary>
        /// Get list of PO for which we can prepare RFP. Note that if a PO already has an RFP that is pending approval and/or authorization then we cannot prepare
        /// and RFP for it. Secondly, if a PO no longer has BudgetCommitments then we cannot also prepare any other RFP for it. If the PO's RFP has been rejected 
        /// then we can prepared another one for it.
        /// </summary>
        /// <param name="cpId"></param>
        /// <returns></returns>
        public List<Model.PurchaseOrder> GetPurchaseOrdersForRFP(Guid cpId)
        {
            using (var context = SCMSEntities.Define())
            {
                return (from pos in context.PurchaseOrders
                        where !(from rfp in context.PaymentRequests
                                where rfp.CountryProgrammeId == cpId && (rfp.IsReviewed == false || rfp.IsAuthorized == false
                                || rfp.IsRejected == true)
                                select (Guid)rfp.PurchaseOrderId).Contains(pos.Id)
                               && (from poItem in context.PurchaseOrderItems
                                   where (from bc in context.BudgetCommitments select bc.PurchaseOrderItemId).Contains(poItem.Id)
                                   select poItem.PurchaseOrderId).Contains(pos.Id)
                        select pos).Where(p => p.CountryProgrammeId == cpId && p.IsAuthorized == true).ToList<Model.PurchaseOrder>();
            }
        }

        public List<Model.PurchaseOrder> GetPurchaseOrdersForReview(SystemUser currentUser)
        {
            try
            {
                using (var context = new SCMSEntities())
                {
                    List<Model.PurchaseOrder> purchaseOrders = new List<Model.PurchaseOrder>();
                    context.SystemUsers.Attach(currentUser);
                    var approvers = context.Approvers.Where(a => a.UserId == currentUser.Id && a.ActivityCode == NotificationHelper.poCode && a.ActionType == NotificationHelper.reviewCode).ToList();
                    var delegateApprovers = context.Approvers.Where(a => a.AssistantId == currentUser.Id && a.ActivityCode == NotificationHelper.poCode && a.ActionType == NotificationHelper.reviewCode).ToList();
                    if (approvers != null)
                    {
                        foreach (var approver in approvers)
                        {
                            var poList = context.PurchaseOrders.Where(p => p.CountryProgrammeId == currentUser.Staff.CountrySubOffice.CountryProgrammeId && (p.IsReviewed == false && p.IsSubmitted == true && p.IsRejected == false)
                                && p.Notifications.Where(n => (Guid)n.PurchaseOrderId == p.Id && n.IsRespondedTo == false && n.SentToDelegate == false && n.ApproverId == approver.Id).Count() > 0).ToList();
                            foreach (var po in poList)
                            {
                                purchaseOrders.Add(po);
                            }
                        }
                    }
                    if (delegateApprovers != null)
                    {
                        foreach (var delegateApprover in delegateApprovers)
                        {
                            var poList = context.PurchaseOrders.Where(p => p.CountryProgrammeId == currentUser.Staff.CountrySubOffice.CountryProgrammeId && (p.IsReviewed == false && p.IsSubmitted == true && p.IsRejected == false)
                                && p.Notifications.Where(n => (Guid)n.PurchaseOrderId == p.Id && n.IsRespondedTo == false && n.SentToDelegate == true && n.ApproverId == delegateApprover.Id).Count() > 0).ToList();
                            foreach (var po in poList)
                            {
                                purchaseOrders.Add(po);
                            }
                        }
                    }
                    return purchaseOrders;
                }
            }
            catch (Exception ex)
            {
                return new List<Model.PurchaseOrder>();
            }
        }

        public List<Model.PurchaseOrder> GetPurchaseOrdersForAuth(SystemUser currentUser)
        {
            try
            {
                using (var context = new SCMSEntities())
                {
                    List<Model.PurchaseOrder> purchaseOrders = new List<Model.PurchaseOrder>();
                    context.SystemUsers.Attach(currentUser);
                    var approvers = context.Approvers.Where(a => a.UserId == currentUser.Id && a.ActivityCode == NotificationHelper.poCode && a.ActionType == NotificationHelper.authorizationCode).ToList();
                    var delegateApprovers = context.Approvers.Where(a => a.AssistantId == currentUser.Id && a.ActivityCode == NotificationHelper.poCode && a.ActionType == NotificationHelper.authorizationCode).ToList();
                    if (approvers != null)
                    {
                        foreach (var approver in approvers)
                        {
                            var poList = context.PurchaseOrders.Where(p => p.CountryProgrammeId == currentUser.Staff.CountrySubOffice.CountryProgrammeId && (p.IsReviewed == true && p.IsRejected == false && p.IsAuthorized != null && p.IsAuthorized == false)
                                && p.Notifications.Where(n => (Guid)n.PurchaseOrderId == p.Id && n.IsRespondedTo == false && n.SentToDelegate == false && n.ApproverId == approver.Id).Count() > 0).ToList();
                            foreach (var po in poList)
                            {
                                purchaseOrders.Add(po);
                            }
                        }
                    }
                    if (delegateApprovers != null)
                    {
                        foreach (var delegateApprover in delegateApprovers)
                        {
                            var poList = context.PurchaseOrders.Where(p => p.CountryProgrammeId == currentUser.Staff.CountrySubOffice.CountryProgrammeId && (p.IsReviewed == true && p.IsRejected == false && p.IsAuthorized != null && p.IsAuthorized == false)
                                && p.Notifications.Where(n => (Guid)n.PurchaseOrderId == p.Id && n.IsRespondedTo == false && n.SentToDelegate == true && n.ApproverId == delegateApprover.Id).Count() > 0).ToList();
                            foreach (var po in poList)
                            {
                                purchaseOrders.Add(po);
                            }
                        }
                    }
                    return purchaseOrders;
                }
            }
            catch (Exception ex)
            {
                return new List<Model.PurchaseOrder>();
            }
        }

        public bool SaveReviewedPOItem(Model.PurchaseOrderItem poItem)
        {
            using (var context = new SCMSEntities())
            {
                context.PurchaseOrderItems.Attach(poItem);
                context.ObjectStateManager.ChangeObjectState(poItem, EntityState.Modified);
                return (context.SaveChanges() > 0) ? true : false;
            }
        }

        public bool SaveReviewedPO(Model.PurchaseOrder po)
        {
            using (var context = new SCMSEntities())
            {
                context.PurchaseOrders.Attach(po);
                context.ObjectStateManager.ChangeObjectState(po, EntityState.Modified);
                return context.SaveChanges() > 0;
            }
        }


        public List<Model.PurchaseOrder> Search(String refNum)
        {
            using (var dbContext = new SCMSEntities())
            {

                List<Model.PurchaseOrder> orlist = dbContext.PurchaseOrders
                    .Where(p => p.RefNumber.Contains(refNum))
                    .OrderByDescending(p => p.PreparedOn).ToList();
                return orlist;
            }
        }

        public bool AuthorizePurchaseOrder(Model.PurchaseOrder po)
        {
            decimal? amount;
            decimal? currAmount;//Amount in ProjectBudget currency
            BudgetCommitment orCommit;
            using (var context = new SCMSEntities())
            {
                var poItems = context.PurchaseOrderItems.Where(p => p.PurchaseOrderId == po.Id).ToList();
                foreach (var poItem in poItems)
                {
                    amount = poItem.TotalPrice;
                    //Get PO Item amount in Budget Currency
                    currAmount = exchangeRateService.GetForeignCurrencyValue(poItem.ProjectBudget.BudgetCategory.ProjectDonor.Currency, poItem.PurchaseOrder.Currency, amount, (Guid)po.CountryProgrammeId);
                    orCommit = new BudgetCommitment();
                    orCommit.AmountCommitted = 0;
                    //Get amount committed by OrderRequestItem
                    if (poItem.OrderRequestItem != null)
                    {
                        var commitmentList = poItem.OrderRequestItem.BudgetCommitments.ToList<BudgetCommitment>();
                        if (commitmentList.Count > 0)
                        {
                            //List should have exactly one item
                            foreach (var commitment in commitmentList)
                            {
                                orCommit = commitment;
                                break;
                            }
                            //Remove initial commitment by OR Item
                            poItem.OrderRequestItem.ProjectBudget.TotalCommitted -= orCommit.AmountCommitted;
                            context.BudgetCommitments.DeleteObject(orCommit);
                        }
                    }
                    //Add commitment by PO Item
                    poItem.ProjectBudget.TotalCommitted += currAmount;

                    //Add to BudgetCommitment table
                    var budgetCommitment = new BudgetCommitment();
                    budgetCommitment.Id = Guid.NewGuid();
                    budgetCommitment.PurchaseOrderItemId = poItem.Id;
                    budgetCommitment.AmountCommitted = (decimal)currAmount;
                    budgetCommitment.DateCommitted = DateTime.Now;
                    budgetCommitment.BudgetLineId = poItem.ProjectBudget.Id;
                    context.BudgetCommitments.AddObject(budgetCommitment);
                }
                return (context.SaveChanges() > 0) ? true : false;
            }

        }

        public List<PurchaseOrderSummary> Find(List<Guid> ids)
        {
            List<PurchaseOrderSummary> purchases = new List<PurchaseOrderSummary>();

            using (var context = new SCMSEntities())
            {
                var results = from myOrders in context.PurchaseOrders
                              where ids.Contains(myOrders.Id)
                              select myOrders;
                //context.ExecuteStoreQuery<OrderRequestView>("SELECT * FROM SomeView").ToList();

                foreach (Model.PurchaseOrder pItem in results.ToList())
                {
                    PurchaseOrderSummary tmp = new PurchaseOrderSummary();
                    tmp.Id = pItem.Id;
                    tmp.OrderRequestRefNumber = pItem.OrderRequest.RefNumber;
                    tmp.RequestDate = pItem.PODate;
                    Staff authStaff = context.Staffs.SingleOrDefault(p => p.Id == pItem.AuthorizedBy);
                    Staff reqStaff = context.Staffs.SingleOrDefault(p => p.Id == pItem.PreparedBy);
                    if (reqStaff != null)
                    {
                        tmp.Requester = reqStaff.Person.FirstName + " " + reqStaff.Person.OtherNames;
                    }

                    if (authStaff != null)
                    {
                        tmp.Authorizer = authStaff.Person.FirstName + " " + authStaff.Person.OtherNames;
                    }
                    tmp.TotalValue = pItem.TotalAmount.HasValue ? ((decimal)pItem.TotalAmount).ToString("#,###.00") : "";

                    if (pItem.IsAuthorized.HasValue && ((bool)pItem.IsAuthorized))
                    {
                        tmp.Status = "AU";
                        tmp.StatusDate = (DateTime)pItem.AuthorizedOn;
                    }
                    else if (pItem.IsReviewed.HasValue && ((bool)pItem.IsReviewed))
                    {
                        tmp.Status = "RV";

                        tmp.StatusDate = (DateTime)pItem.ReviewedOn;
                    }
                    else if (pItem.IsRejected)
                    {
                        tmp.Status = "RJ";

                        if (pItem.AuthorizedOn.HasValue)
                        {
                            tmp.StatusDate = (DateTime)pItem.AuthorizedOn;
                        }
                        else
                        {
                            tmp.StatusDate = (DateTime)pItem.ReviewedOn;
                        }

                    }
                    else if (pItem.IsSubmitted)
                    {
                        tmp.Status = "CR";
                        tmp.StatusDate = (DateTime)pItem.PreparedOn;
                    }
                    else
                    {
                        tmp.Status = "NEW";
                        tmp.StatusDate = (DateTime)pItem.PreparedOn;
                    }

                    purchases.Add(tmp);
                }

            }

            return purchases;

        }
    }
}
